<template>
  <div class="bom-processor">
    <slot />
  </div>
</template>

<script lang="ts" setup>
import { multiFieldSort } from '@/utils';
import ExcelJS from 'exceljs';
import JSZip from 'jszip';
import * as XLSX from 'xlsx';

export interface BomData {
  materialNo: string; // 物料编号
  bondedFlag: string; // 保税标识
  level: number; // 层级
  processNo: string; // 制程工段编号
  utilization: string; // 利用率
  usage: number; // 用量
  unit: string; // 单位
  startDate: string; // 有效起始时间
  endDate: string; // 有效结束时间
  parentMaterialNo: string; // 上一层级物料编号
  rootMaterialNo: string; // 根节点物料编号
  substituteGroup?: string; // 替代组
  bulkMaterial?: string; // Bulk Material
  substitutePriority?: number; // 替代组优先级
  phantom?: string; // 虚拟料
}

const props = defineProps({
  file: {
    type: File,
    required: true
  },
  templateFile: {
    type: File,
    default: null
  },
  materialRefFile: {
    type: File,
    default: null
  },
  topNodes: {
    type: String,
    default: ''
  },
  splitSize: {
    type: Number,
    default: 1000
  }
});

const emit = defineEmits(['progress', 'error', 'complete']);

const processBomData = async () => {
  try {
    emit('progress', '开始读取Excel文件...');
    const data = await readSourceExcel(props.file);

    emit('progress', '合并BOM数据...');
    const mergedData = mergeBomData(data);

    emit('progress', '处理数据格式...');
    const processedData = processDataFormat(mergedData);

    emit('progress', '筛选数据...');
    const filteredData = filterByTopNodes(processedData);

    // 如果有物料参考文件，处理物料单位信息
    if (props.materialRefFile) {
      emit('progress', '读取物料参考文件...');
      const materialRefData = await readMaterialRefExcel(props.materialRefFile);
      emit('progress', '更新物料单位信息...');
      updateMaterialUnits(filteredData, materialRefData);
    }

    emit('progress', `导出数据共计：${filteredData.length}`);

    emit('progress', '分割数据...');
    const splitData = splitBomData(filteredData);

    emit('progress', '准备导出数据...');
    const exportResults = await exportToExcel(splitData);

    emit('complete', exportResults);
  } catch (error) {
    emit('error', error);
  }
};

const formatDate = (date: any): string => {
  if (date instanceof Date) {
    return date.toISOString().split('T')[0];
  }
  if (typeof date === 'number') {
    return new Date((date - 25569) * 86400 * 1000).toISOString().split('T')[0];
  }
  if (typeof date === 'string' && date.includes('/')) {
    // 处理类似 "2999/12/31" 的日期字符串格式，直接转换格式，不考虑时区
    const [year, month, day] = date.split('/');
    return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
  }
  return date;
};

// 处理利用率字段，支持数字和百分比字符串
const parseUtilization = (value: any): string => {
  if (value === null || value === undefined) {
    return '100'; // 默认值
  }

  if (typeof value === 'number') {
    // 如果是数字，直接转换为字符串（去掉小数部分）
    return Math.round(value).toString();
  }

  if (typeof value === 'string') {
    // 如果是字符串，移除百分号并处理
    const cleanedValue = value.replace('%', '').trim();
    return cleanedValue;
  }

  // 如果无法解析，返回默认值
  return '100';
};

const readSourceExcel = (file: File): Promise<any> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.onload = e => {
      try {
        const data = e.target?.result;
        const workbook = XLSX.read(data, { type: 'array' });

        // 检查必要的工作表是否存在
        if (!workbook.Sheets['LVL1以降'] || !workbook.Sheets['BOM']) {
          throw new Error('Excel文件中缺少必要的工作表(LVL1以降或BOM)');
        }

        resolve(workbook);
      } catch (error) {
        reject(error);
      }
    };

    reader.onerror = () => {
      reject(new Error('文件读取失败'));
    };

    reader.readAsArrayBuffer(file);
  });

const readTemplateExcel = (file: File): Promise<ExcelJS.Workbook> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.onload = async e => {
      try {
        const data = e.target?.result as ArrayBuffer;
        const workbook = new ExcelJS.Workbook();
        await workbook.xlsx.load(data);
        resolve(workbook);
      } catch (error) {
        reject(error);
      }
    };

    reader.onerror = () => {
      reject(new Error('文件读取失败'));
    };

    reader.readAsArrayBuffer(file);
  });

const readMaterialRefExcel = (file: File): Promise<Map<string, string>> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.onload = e => {
      try {
        const data = e.target?.result;
        const workbook = XLSX.read(data, { type: 'array' });

        // 获取第一个工作表
        const firstSheetName = workbook.SheetNames[0];
        const sheet = workbook.Sheets[firstSheetName];

        // 读取所有数据，从第3行开始（跳过前2行）
        const jsonData = XLSX.utils.sheet_to_json(sheet, {
          range: 2, // 从第3行开始（0-based索引，2表示第3行）
          header: 2
        });

        // 创建物料编号+保税标识到基本单位的映射
        const materialMap = new Map<string, string>();

        jsonData.forEach((item: any) => {
          const materialNo = item['物料编号'];
          const bondedFlag = item['保税标识']?.toString().endsWith('2') ? '是' : '否';
          const baseUnit = item['基本单位'];

          if (materialNo && bondedFlag && baseUnit) {
            const key = `${materialNo}_${bondedFlag}`;
            materialMap.set(key, baseUnit);
          }
        });

        resolve(materialMap);
      } catch (error) {
        reject(error);
      }
    };

    reader.onerror = () => {
      reject(new Error('物料参考文件读取失败'));
    };

    reader.readAsArrayBuffer(file);
  });

const updateMaterialUnits = (bomData: BomData[], materialRefData: Map<string, string>) => {
  let count = 0;
  bomData.forEach(item => {
    // 只处理层级为0的顶节点
    if (item.level !== 0) {
      return;
    }
    const key = `${item.materialNo}_${item.bondedFlag}`;
    const baseUnit = materialRefData.get(key);

    if (baseUnit) {
      item.unit = baseUnit;
      count++;
    }
  });
  emit('progress', `自动匹配根节点物料单位, 共更新${count}条`);
};

const mergeBomData = (workbook: XLSX.WorkBook): BomData[] => {
  emit('progress', '读取LVL1以降工作表...');
  const lvl1Sheet = workbook.Sheets['LVL1以降'];
  const lvl1Data = XLSX.utils.sheet_to_json(lvl1Sheet, { header: 0 }); // 第1行为表头（header=0）

  emit('progress', '读取BOM工作表...');
  const bomSheet = workbook.Sheets['BOM'];
  const bomData = XLSX.utils.sheet_to_json(bomSheet, { header: 0 }); // 第1行为表头（header=0）

  emit('progress', `源数据1条数总计：${lvl1Data.length};源数据2条数总计：${bomData.length}`);

  emit('progress', '合并数据...');

  // 过滤物料编号为0的行
  const filteredLvl1Data = lvl1Data.filter(
    (item: any) => item['物料编号'] && item['物料编号'] !== 0
  );
  const filteredBomData = bomData.filter((item: any) => item['物料编号'] && item['物料编号'] !== 0);

  // 将两个表的数据整合成相同的结构
  const transferLvl1Data = filteredLvl1Data.map((item: any) => ({
    materialNo: item['物料编号'], // 物料编号
    bondedFlag: item['Plant']?.toString().endsWith('2') ? '是' : '否', // 保税标识 (Plant以2结尾为是)
    level: Number(item['层级']), // 层级
    processNo: item['制程工段编号'], // 制程工段编号
    utilization: parseUtilization(item['利用率']), // 处理利用率字段
    usage: item['用量'] / 1000, // 用量 (除以1000转换单位)
    unit: item['用量单位'] || 'PCE', // 单位
    startDate: formatDate(item['有效起始时间']), // 有效起始时间 (格式化日期)
    endDate: formatDate(item['有效终止时间']), // 有效结束时间
    parentMaterialNo: item['上一层级物料编号'], // 上一层级物料编号
    rootMaterialNo: item['根节点物料编号'], // 根节点物料编号
    substituteGroup: item['替代组'], // 替代组
    bulkMaterial: item['Bulk Material'], // Bulk Material
    substitutePriority: item['替代组优先级'], // 替代组优先级
    phantom: item['Phantom'] === 'x' ? '是' : '否' // 虚拟料
  }));
  const transferBomData = filteredBomData.map((item: any) => ({
    materialNo: item['物料编号'], // 物料编号
    bondedFlag: item['Plant']?.toString().endsWith('2') ? '是' : '否', // 保税标识 (Plant以2结尾为是)
    level: Number(item['层级']), // 层级
    processNo: item['制程工段编号'], // 制程工段编号
    utilization: '100', // 利用率
    usage: item['用量'] || '1', // 用量 (除以1000转换单位)
    unit: item['用量单位'], // 单位
    startDate: formatDate(item['有效起始时间']), // 有效起始时间 (格式化日期)
    endDate: formatDate(item['有效终止时间']), // 有效结束时间
    parentMaterialNo: item['上一层级物料编号'], // 上一层级物料编号
    rootMaterialNo: item['根节点物料编号'], // 根节点物料编号
    phantom: '否' // 虚拟料（固定值：否）
  }));

  // 更新替代组优先级
  emit('progress', '更新替代组优先级...');
  updateSubstitutePriority(transferLvl1Data);

  // 合并数据
  const combinedData = [...transferLvl1Data, ...transferBomData];

  // 筛选两个表中指定顶节点物料
  if (props.topNodes) {
    const topNodesList = props.topNodes.split(',').map(item => item.trim());
    const filteredData = combinedData.filter(item => topNodesList.includes(item.rootMaterialNo));

    if (filteredData.length === 0) {
      throw new Error(`未找到指定的顶节点物料: ${props.topNodes}`);
    }

    combinedData.length = 0;
    combinedData.push(...filteredData);
  }

  return combinedData;
};

// 更新替代组优先级
const updateSubstitutePriority = (data: BomData[]): void => {
  // 按根节点物料编号分组
  const groups: { [key: string]: BomData[] } = {};
  data.forEach(item => {
    if (!groups[item.rootMaterialNo]) {
      groups[item.rootMaterialNo] = [];
    }
    groups[item.rootMaterialNo].push(item);
  });

  // 处理每个分组
  Object.values(groups).forEach(group => {
    // 按替代组分组
    const substituteGroups: { [key: string]: BomData[] } = {};
    group.forEach(item => {
      const groupKey = item.substituteGroup || '';
      if (!substituteGroups[groupKey]) {
        substituteGroups[groupKey] = [];
      }
      substituteGroups[groupKey].push(item);
    });

    // 处理每个替代组
    Object.values(substituteGroups).forEach(subGroup => {
      // 只处理替代组不为空的数据
      if (subGroup[0]?.substituteGroup) {
        // 如果组内有替代组相同的数据
        if (subGroup.length > 1) {
          // 检查是否有Bulk Material = 'X'的数据
          // const hasBulkMaterialX = subGroup.some(item => item.bulkMaterial === 'X');

          // 更新优先级 - 按顺序从2开始依次+1
          let priority = 2;
          subGroup.forEach(item => {
            if (item.bulkMaterial === 'X') {
              item.substitutePriority = priority;
              priority++;
            } else {
              item.substitutePriority = 1; // 非Bulk Material = 'X'的优先级保持为1
            }
          });
        }
      }
    });
  });
};

const processDataFormat = (data: BomData[]): BomData[] => {
  emit('progress', '处理数据格式...');

  // 合并后，根据根Plant、根节点物料编号、层级、物料编号进行排序（升序）
  return multiFieldSort(data, [
    {
      field: 'bondedFlag',
      order: 'asc'
    },
    {
      field: 'rootMaterialNo',
      order: 'asc'
    },
    {
      field: 'level',
      order: 'asc'
    },
    {
      field: 'materialNo',
      order: 'asc'
    }
  ]);
};

const filterByTopNodes = (data: BomData[]): BomData[] =>
  // 已经在mergeBomData中处理了顶节点筛选
  data;
const exportToExcel = async (dataChunks: BomData[][]): Promise<string[]> => {
  const exportedFiles: string[] = [];

  if (!props.templateFile) {
    throw new Error('请先选择模板Excel文件');
  }

  const templateWorkbook = await readTemplateExcel(props.templateFile);

  // 如果只有一个文件且不分割，直接导出单个文件
  if (dataChunks.length === 1 && props.splitSize === 0) {
    const chunk = dataChunks[0];
    emit('progress', '导出文件...');

    // 克隆模板工作簿以保持样式
    const workbook = new ExcelJS.Workbook();
    templateWorkbook.eachSheet((templateSheet, sheetId) => {
      const newSheet = workbook.addWorksheet(templateSheet.name || `Sheet${sheetId}`);

      // 首先复制所有合并单元格
      if (templateSheet.model.merges) {
        templateSheet.model.merges.forEach(merge => {
          newSheet.mergeCells(merge);
        });
      }

      // 复制所有行和单元格样式
      templateSheet.eachRow((templateRow, rowNumber) => {
        const newRow = newSheet.getRow(rowNumber);

        templateRow.eachCell((templateCell, colNumber) => {
          const newCell = newRow.getCell(colNumber);
          newCell.value = templateCell.value;

          // 复制单元格样式
          if (templateCell.style) {
            newCell.style = JSON.parse(JSON.stringify(templateCell.style));
          }
        });
      });

      // 复制列宽
      newSheet.columns = templateSheet.columns;
    });

    // 获取第一个工作表
    const worksheet = workbook.getWorksheet(1);

    // 从第4行开始写入数据
    // A列: 物料编号, B列: 保税标识, C列: 层级, D列: 制程工段编号
    // E列: 利用率, F列: 用量, G列: 单位, H列: 有效起始时间
    // I列: 有效结束时间, J列: 替代组, K列: Bulk Material, L列: 替代组优先级, M列: 上一层级物料编号, N列: 根节点物料编号
    chunk.forEach((item, index) => {
      const rowNumber = index + 4;

      worksheet.getCell(`A${rowNumber}`).value = item.materialNo; // 物料编号
      worksheet.getCell(`B${rowNumber}`).value = item.bondedFlag; // 保税标识
      worksheet.getCell(`C${rowNumber}`).value = item.level; // 层级
      worksheet.getCell(`D${rowNumber}`).value = item.processNo; // 制程工段编号
      worksheet.getCell(`E${rowNumber}`).value = item.utilization; // 利用率
      worksheet.getCell(`F${rowNumber}`).value = item.usage; // 用量
      worksheet.getCell(`G${rowNumber}`).value = item.unit; // 单位
      worksheet.getCell(`H${rowNumber}`).value = item.startDate; // 有效起始时间
      worksheet.getCell(`I${rowNumber}`).value = item.endDate; // 有效结束时间
      worksheet.getCell(`J${rowNumber}`).value = item.substituteGroup; // 替代组
      worksheet.getCell(`K${rowNumber}`).value = item.substitutePriority; // 替代组优先级
      worksheet.getCell(`L${rowNumber}`).value = item.parentMaterialNo; // 上一层级物料编号
      worksheet.getCell(`M${rowNumber}`).value = item.rootMaterialNo; // 根节点物料编号
      worksheet.getCell(`N${rowNumber}`).value = item.phantom; // 虚拟料
    });

    const fileName = `BOM_${new Date().toISOString().replace(/[:.]/g, '-')}.xlsx`;
    const buffer = await workbook.xlsx.writeBuffer();

    // 保存文件
    const blob = new Blob([buffer], {
      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    const url = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = fileName;
    link.click();
    window.URL.revokeObjectURL(url);

    exportedFiles.push(fileName);
    return exportedFiles;
  }

  // 多个文件时打包成zip
  const zip = new JSZip();

  for (let i = 0; i < dataChunks.length; i++) {
    const chunk = dataChunks[i];
    emit('progress', `导出第${i + 1}个文件...`);

    // 克隆模板工作簿以保持样式
    const workbook = new ExcelJS.Workbook();
    templateWorkbook.eachSheet((templateSheet, sheetId) => {
      const newSheet = workbook.addWorksheet(templateSheet.name || `Sheet${sheetId}`);

      // 首先复制所有合并单元格
      if (templateSheet.model.merges) {
        templateSheet.model.merges.forEach(merge => {
          newSheet.mergeCells(merge);
        });
      }

      // 复制所有行和单元格样式
      templateSheet.eachRow((templateRow, rowNumber) => {
        const newRow = newSheet.getRow(rowNumber);

        templateRow.eachCell((templateCell, colNumber) => {
          const newCell = newRow.getCell(colNumber);
          newCell.value = templateCell.value;

          // 复制单元格样式
          if (templateCell.style) {
            newCell.style = JSON.parse(JSON.stringify(templateCell.style));
          }
        });
      });

      // 复制列宽
      newSheet.columns = templateSheet.columns;
    });

    // 获取第一个工作表
    const worksheet = workbook.getWorksheet(1);

    // 从第4行开始写入数据
    // A列: 物料编号, B列: 保税标识, C列: 层级, D列: 制程工段编号
    // E列: 利用率, F列: 用量, G列: 单位, H列: 有效起始时间
    // I列: 有效结束时间, J列: 替代组, K列: Bulk Material, L列: 替代组优先级, M列: 上一层级物料编号, N列: 根节点物料编号
    chunk.forEach((item, index) => {
      const rowNumber = index + 4;

      worksheet.getCell(`A${rowNumber}`).value = item.materialNo; // 物料编号
      worksheet.getCell(`B${rowNumber}`).value = item.bondedFlag; // 保税标识
      worksheet.getCell(`C${rowNumber}`).value = item.level; // 层级
      worksheet.getCell(`D${rowNumber}`).value = item.processNo; // 制程工段编号
      worksheet.getCell(`E${rowNumber}`).value = item.utilization; // 利用率
      worksheet.getCell(`F${rowNumber}`).value = item.usage; // 用量
      worksheet.getCell(`G${rowNumber}`).value = item.unit; // 单位
      worksheet.getCell(`H${rowNumber}`).value = item.startDate; // 有效起始时间
      worksheet.getCell(`I${rowNumber}`).value = item.endDate; // 有效结束时间
      worksheet.getCell(`J${rowNumber}`).value = item.substituteGroup; // 替代组
      worksheet.getCell(`K${rowNumber}`).value = item.substitutePriority; // 替代组优先级
      worksheet.getCell(`L${rowNumber}`).value = item.parentMaterialNo; // 上一层级物料编号
      worksheet.getCell(`M${rowNumber}`).value = item.rootMaterialNo; // 根节点物料编号
    });

    const fileName = `BOM_${new Date().toISOString().replace(/[:.]/g, '-')}_${i + 1}.xlsx`;
    const buffer = await workbook.xlsx.writeBuffer();

    // 添加到zip文件
    zip.file(fileName, buffer);
    exportedFiles.push(fileName);
  }

  // 生成并下载zip文件
  emit('progress', '生成zip文件...');
  const zipContent = await zip.generateAsync({ type: 'blob' });
  const zipUrl = window.URL.createObjectURL(zipContent);
  const zipLink = document.createElement('a');
  zipLink.href = zipUrl;
  zipLink.download = `BOM_${new Date().toISOString().replace(/[:.]/g, '-')}.zip`;
  zipLink.click();
  window.URL.revokeObjectURL(zipUrl);

  return exportedFiles;
};

const splitBomData = (data: BomData[]): BomData[][] => {
  if (data.length === 0) return [];

  // 如果splitSize为0，则不分割，直接返回整个数据集
  if (props.splitSize === 0) {
    return [data];
  }

  const maxExcelRows = 1048576; // Excel最大行数限制

  // 按根节点物料编号和保税标识分组
  const groups: { [key: string]: BomData[] } = {};
  data.forEach(item => {
    const groupKey = `${item.rootMaterialNo}_${item.bondedFlag}`;
    if (!groups[groupKey]) {
      groups[groupKey] = [];
    }
    groups[groupKey].push(item);
  });

  // 检查是否有组超过Excel限制
  Object.entries(groups).forEach(([key, group]) => {
    if (group.length > maxExcelRows - 3) {
      emit(
        'error',
        `根节点物料 ${group[0].rootMaterialNo} (保税标识: ${group[0].bondedFlag}) 的数据量(${group.length})超过Excel单文件限制(${maxExcelRows - 3})`
      );
    }
  });

  const result: BomData[][] = [];

  // 按分组顺序处理数据
  Object.values(groups).forEach(group => {
    const groupSize = group.length;

    // 如果整个组的大小超过分割大小，需要将组内数据进一步分割
    if (groupSize > props.splitSize) {
      // 将大组分割成多个小块
      for (let i = 0; i < group.length; i += props.splitSize) {
        const chunk = group.slice(i, i + props.splitSize);
        result.push(chunk);
      }
    } else {
      // 尝试将小组添加到现有结果中
      let addedToExisting = false;

      // 检查是否可以添加到最后一个文件
      if (result.length > 0) {
        const lastChunk = result[result.length - 1];
        if (lastChunk.length + groupSize <= props.splitSize) {
          // 可以添加到最后一个文件
          lastChunk.push(...group);
          addedToExisting = true;
        }
      }

      // 如果不能添加到现有文件，创建新文件
      if (!addedToExisting) {
        result.push([...group]);
      }
    }
  });

  return result;
};

defineExpose({ processBomData });
</script>

<style scoped>
.bom-processor {
  width: 100%;
}
</style>
